home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkCanvLine.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-28  |  42.6 KB  |  1,453 lines

  1. /* 
  2.  * tkCanvLine.c --
  3.  *
  4.  *    This file implements line items for canvas widgets.
  5.  *
  6.  * Copyright 1991-1992 Regents of the University of California.
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvLine.c,v 1.7 92/07/28 15:40:08 ouster Exp $ SPRITE (Berkeley)";
  18. #endif
  19.  
  20. #include <stdio.h>
  21. #include <math.h>
  22. #include "tkInt.h"
  23. #include "tkCanvas.h"
  24. #include "tkConfig.h"
  25.  
  26. /*
  27.  * The structure below defines the record for each line item.
  28.  */
  29.  
  30. typedef struct LineItem  {
  31.     Tk_Item header;        /* Generic stuff that's the same for all
  32.                  * types.  MUST BE FIRST IN STRUCTURE. */
  33.     Tk_Canvas *canvasPtr;    /* Canvas containing item.  Needed for
  34.                  * parsing arrow shapes. */
  35.     int numPoints;        /* Number of points in line (always >= 2). */
  36.     double *coordPtr;        /* Pointer to malloc-ed array containing
  37.                  * x- and y-coords of all points in line.
  38.                  * X-coords are even-valued indices, y-coords
  39.                  * are corresponding odd-valued indices. */
  40.     int width;            /* Width of line. */
  41.     XColor *fg;            /* Foreground color for line. */
  42.     Pixmap fillStipple;        /* Stipple bitmap for filling line. */
  43.     int capStyle;        /* Cap style for line. */
  44.     int joinStyle;        /* Join style for line. */
  45.     GC gc;            /* Graphics context for filling line. */
  46.     Tk_Uid arrow;        /* Indicates whether or not to draw arrowheads:
  47.                  * "none", "first", "last", or "both". */
  48.     float arrowShapeA;        /* Distance from tip of arrowhead to center. */
  49.     float arrowShapeB;        /* Distance from tip of arrowhead to trailing
  50.                  * point, measured along shaft. */
  51.     float arrowShapeC;        /* Distance of trailing points from outside
  52.                  * edge of shaft. */
  53.     double *firstArrowPtr;    /* Points to array of 5 points describing
  54.                  * polygon for arrowhead at first point in
  55.                  * line.  First point of arrowhead is tip.
  56.                  * Malloc'ed.  NULL means no arrowhead at
  57.                  * first point. */
  58.     double *lastArrowPtr;    /* Points to polygon for arrowhead at last
  59.                  * point in line (5 points, first of which
  60.                  * is tip).  Malloc'ed.  NULL means no
  61.                  * arrowhead at last point. */
  62.     int smooth;            /* Non-zero means draw line smoothed (i.e.
  63.                  * with Bezier splines). */
  64.     int splineSteps;        /* Number of steps in each spline segment. */
  65. } LineItem;
  66.  
  67. /*
  68.  * Number of points in an arrowHead:
  69.  */
  70.  
  71. #define PTS_IN_ARROW 6
  72.  
  73. /*
  74.  * Prototypes for procedures defined in this file:
  75.  */
  76.  
  77. static void        ComputeLineBbox _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  78.                 LineItem *linePtr));
  79. static int        ConfigureLine _ANSI_ARGS_((
  80.                 Tk_Canvas *canvasPtr, Tk_Item *itemPtr, int argc,
  81.                 char **argv, int flags));
  82. static int        ConfigureArrows _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  83.                 LineItem *linePtr));
  84. static int        CreateLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  85.                 struct Tk_Item *itemPtr, int argc, char **argv));
  86. static void        DeleteLine _ANSI_ARGS_((Tk_Item *itemPtr));
  87. static void        DisplayLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  88.                 Tk_Item *itemPtr, Drawable dst));
  89. static int        LineCoords _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  90.                 Tk_Item *itemPtr, int argc, char **argv));
  91. static int        LineToArea _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  92.                 Tk_Item *itemPtr, double *rectPtr));
  93. static double        LineToPoint _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  94.                 Tk_Item *itemPtr, double *coordPtr));
  95. static int        ParseArrowShape _ANSI_ARGS_((ClientData clientData,
  96.                 Tcl_Interp *interp, Tk_Window tkwin, char *value,
  97.                 char *recordPtr, int offset));
  98. static char *        PrintArrowShape _ANSI_ARGS_((ClientData clientData,
  99.                 Tk_Window tkwin, char *recordPtr, int offset,
  100.                 Tcl_FreeProc **freeProcPtr));
  101. static void        ScaleLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  102.                 Tk_Item *itemPtr, double originX, double originY,
  103.                 double scaleX, double scaleY));
  104. static void        TranslateLine _ANSI_ARGS_((Tk_Canvas *canvasPtr,
  105.                 Tk_Item *itemPtr, double deltaX, double deltaY));
  106.  
  107. /*
  108.  * Information used for parsing configuration specs.  If you change any
  109.  * of the default strings, be sure to change the corresponding default
  110.  * values in CreateLine.
  111.  */
  112.  
  113. static Tk_CustomOption arrowShapeOption = {ParseArrowShape,
  114.     PrintArrowShape, (ClientData) NULL};
  115.  
  116. static Tk_ConfigSpec configSpecs[] = {
  117.     {TK_CONFIG_UID, "-arrow", (char *) NULL, (char *) NULL,
  118.     "none", Tk_Offset(LineItem, arrow), TK_CONFIG_DONT_SET_DEFAULT},
  119.     {TK_CONFIG_CUSTOM, "-arrowshape", (char *) NULL, (char *) NULL,
  120.     "8 10 3", Tk_Offset(LineItem, arrowShapeA),
  121.     TK_CONFIG_DONT_SET_DEFAULT, &arrowShapeOption},
  122.     {TK_CONFIG_CAP_STYLE, "-capstyle", (char *) NULL, (char *) NULL,
  123.     "butt", Tk_Offset(LineItem, capStyle), TK_CONFIG_DONT_SET_DEFAULT},
  124.     {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
  125.     "black", Tk_Offset(LineItem, fg), 0},
  126.     {TK_CONFIG_JOIN_STYLE, "-joinstyle", (char *) NULL, (char *) NULL,
  127.     "round", Tk_Offset(LineItem, joinStyle), TK_CONFIG_DONT_SET_DEFAULT},
  128.     {TK_CONFIG_BOOLEAN, "-smooth", (char *) NULL, (char *) NULL,
  129.     "no", Tk_Offset(LineItem, smooth), TK_CONFIG_DONT_SET_DEFAULT},
  130.     {TK_CONFIG_INT, "-splinesteps", (char *) NULL, (char *) NULL,
  131.     "12", Tk_Offset(LineItem, splineSteps), TK_CONFIG_DONT_SET_DEFAULT},
  132.     {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
  133.     (char *) NULL, Tk_Offset(LineItem, fillStipple), TK_CONFIG_NULL_OK},
  134.     {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
  135.     (char *) NULL, 0, TK_CONFIG_NULL_OK, &tkCanvasTagsOption},
  136.     {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
  137.     "1", Tk_Offset(LineItem, width), TK_CONFIG_DONT_SET_DEFAULT},
  138.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  139.     (char *) NULL, 0, 0}
  140. };
  141.  
  142. /*
  143.  * The structures below defines the line item type by means
  144.  * of procedures that can be invoked by generic item code.
  145.  */
  146.  
  147. Tk_ItemType TkLineType = {
  148.     "line",                /* name */
  149.     sizeof(LineItem),            /* itemSize */
  150.     CreateLine,                /* createProc */
  151.     configSpecs,            /* configSpecs */
  152.     ConfigureLine,            /* configureProc */
  153.     LineCoords,                /* coordProc */
  154.     DeleteLine,                /* deleteProc */
  155.     DisplayLine,            /* displayProc */
  156.     0,                    /* alwaysRedraw */
  157.     LineToPoint,            /* pointProc */
  158.     LineToArea,                /* areaProc */
  159.     (Tk_ItemPostscriptProc *) NULL,    /* postscriptProc */
  160.     ScaleLine,                /* scaleProc */
  161.     TranslateLine,            /* translateProc */
  162.     (Tk_ItemIndexProc *) NULL,        /* indexProc */
  163.     (Tk_ItemCursorProc *) NULL,        /* cursorProc */
  164.     (Tk_ItemSelectionProc *) NULL,    /* selectionProc */
  165.     (Tk_ItemInsertProc *) NULL,        /* insertProc */
  166.     (Tk_ItemDCharsProc *) NULL,        /* dTextProc */
  167.     (Tk_ItemType *) NULL        /* nextPtr */
  168. };
  169.  
  170. /*
  171.  * The Tk_Uid's below refer to uids for the various arrow types:
  172.  */
  173.  
  174. static Tk_Uid noneUid = NULL;
  175. static Tk_Uid firstUid = NULL;
  176. static Tk_Uid lastUid = NULL;
  177. static Tk_Uid bothUid = NULL;
  178.  
  179. /*
  180.  * The definition below determines how large are static arrays
  181.  * used to hold spline points (splines larger than this have to
  182.  * have their arrays malloc-ed).
  183.  */
  184.  
  185. #define MAX_STATIC_POINTS 200
  186.  
  187. /*
  188.  *--------------------------------------------------------------
  189.  *
  190.  * CreateLine --
  191.  *
  192.  *    This procedure is invoked to create a new line item in
  193.  *    a canvas.
  194.  *
  195.  * Results:
  196.  *    A standard Tcl return value.  If an error occurred in
  197.  *    creating the item, then an error message is left in
  198.  *    canvasPtr->interp->result;  in this case itemPtr is
  199.  *    left uninitialized, so it can be safely freed by the
  200.  *    caller.
  201.  *
  202.  * Side effects:
  203.  *    A new line item is created.
  204.  *
  205.  *--------------------------------------------------------------
  206.  */
  207.  
  208. static int
  209. CreateLine(canvasPtr, itemPtr, argc, argv)
  210.     register Tk_Canvas *canvasPtr;    /* Canvas to hold new item. */
  211.     Tk_Item *itemPtr;            /* Record to hold new item;  header
  212.                      * has been initialized by caller. */
  213.     int argc;                /* Number of arguments in argv. */
  214.     char **argv;            /* Arguments describing line. */
  215. {
  216.     register LineItem *linePtr = (LineItem *) itemPtr;
  217.     int i;
  218.  
  219.     if (argc < 4) {
  220.     Tcl_AppendResult(canvasPtr->interp, "wrong # args:  should be \"",
  221.         Tk_PathName(canvasPtr->tkwin),
  222.         "\" create x1 y1 x2 y2 ?x3 y3 ...? ?options?",
  223.         (char *) NULL);
  224.     return TCL_ERROR;
  225.     }
  226.  
  227.     /*
  228.      * Carry out initialization that is needed to set defaults and to
  229.      * allow proper cleanup after errors during the the remainder of
  230.      * this procedure.
  231.      */
  232.  
  233.     linePtr->canvasPtr = canvasPtr;
  234.     linePtr->numPoints = 0;
  235.     linePtr->coordPtr = NULL;
  236.     linePtr->width = 1;
  237.     linePtr->fg = None;
  238.     linePtr->fillStipple = None;
  239.     linePtr->capStyle = CapButt;
  240.     linePtr->joinStyle = JoinRound;
  241.     linePtr->gc = None;
  242.     if (noneUid == NULL) {
  243.     noneUid = Tk_GetUid("none");
  244.     firstUid = Tk_GetUid("first");
  245.     lastUid = Tk_GetUid("last");
  246.     bothUid = Tk_GetUid("both");
  247.     }
  248.     linePtr->arrow = noneUid;
  249.     linePtr->arrowShapeA = 8.0;
  250.     linePtr->arrowShapeB = 10.0;
  251.     linePtr->arrowShapeC = 3.0;
  252.     linePtr->firstArrowPtr = NULL;
  253.     linePtr->lastArrowPtr = NULL;
  254.     linePtr->smooth = 0;
  255.     linePtr->splineSteps = 12;
  256.  
  257.     /*
  258.      * Count the number of points and then parse them into a point
  259.      * array.  Leading arguments are assumed to be points if they
  260.      * start with a digit or a minus sign followed by a digit.
  261.      */
  262.  
  263.     for (i = 4; i < (argc-1); i+=2) {
  264.     if ((!isdigit(argv[i][0])) &&
  265.         ((argv[i][0] != '-') || (!isdigit(argv[i][1])))) {
  266.         break;
  267.     }
  268.     }
  269.     if (LineCoords(canvasPtr, itemPtr, i, argv) != TCL_OK) {
  270.     goto error;
  271.     }
  272.     if (ConfigureLine(canvasPtr, itemPtr, argc-i, argv+i, 0) == TCL_OK) {
  273.     return TCL_OK;
  274.     }
  275.  
  276.     error:
  277.     DeleteLine(itemPtr);
  278.     return TCL_ERROR;
  279. }
  280.  
  281. /*
  282.  *--------------------------------------------------------------
  283.  *
  284.  * LineCoords --
  285.  *
  286.  *    This procedure is invoked to process the "coords" widget
  287.  *    command on lines.  See the user documentation for details
  288.  *    on what it does.
  289.  *
  290.  * Results:
  291.  *    Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result.
  292.  *
  293.  * Side effects:
  294.  *    The coordinates for the given item may be changed.
  295.  *
  296.  *--------------------------------------------------------------
  297.  */
  298.  
  299. static int
  300. LineCoords(canvasPtr, itemPtr, argc, argv)
  301.     register Tk_Canvas *canvasPtr;    /* Canvas containing item. */
  302.     Tk_Item *itemPtr;            /* Item whose coordinates are to be
  303.                      * read or modified. */
  304.     int argc;                /* Number of coordinates supplied in
  305.                      * argv. */
  306.     char **argv;            /* Array of coordinates: x1, y1,
  307.                      * x2, y2, ... */
  308. {
  309.     register LineItem *linePtr = (LineItem *) itemPtr;
  310.     char buffer[300];
  311.     int i, numPoints;
  312.  
  313.     if (argc == 0) {
  314.     for (i = 0; i < 2*linePtr->numPoints; i++) {
  315.         sprintf(buffer, "%g", linePtr->coordPtr[i]);
  316.         Tcl_AppendElement(canvasPtr->interp, buffer, 0);
  317.     }
  318.     } else if (argc < 4) {
  319.     Tcl_AppendResult(canvasPtr->interp,
  320.         "too few coordinates for line:  must have at least 4",
  321.         (char *) NULL);
  322.     return TCL_ERROR;
  323.     } else if (argc & 1) {
  324.     Tcl_AppendResult(canvasPtr->interp,
  325.         "odd number of coordinates specified for line",
  326.         (char *) NULL);
  327.     return TCL_ERROR;
  328.     } else {
  329.     numPoints = argc/2;
  330.     if (linePtr->numPoints != numPoints) {
  331.         if (linePtr->coordPtr != NULL) {
  332.         ckfree((char *) linePtr->coordPtr);
  333.         }
  334.         linePtr->coordPtr = (double *) ckalloc((unsigned)
  335.             (sizeof(double) * argc));
  336.         linePtr->numPoints = numPoints;
  337.     }
  338.     for (i = argc-1; i >= 0; i--) {
  339.         if (TkGetCanvasCoord(canvasPtr, argv[i], &linePtr->coordPtr[i])
  340.             != TCL_OK) {
  341.         return TCL_ERROR;
  342.         }
  343.     }
  344.     ComputeLineBbox(canvasPtr, linePtr);
  345.     }
  346.     return TCL_OK;
  347. }
  348.  
  349. /*
  350.  *--------------------------------------------------------------
  351.  *
  352.  * ConfigureLine --
  353.  *
  354.  *    This procedure is invoked to configure various aspects
  355.  *    of a line item such as its background color.
  356.  *
  357.  * Results:
  358.  *    A standard Tcl result code.  If an error occurs, then
  359.  *    an error message is left in canvasPtr->interp->result.
  360.  *
  361.  * Side effects:
  362.  *    Configuration information, such as colors and stipple
  363.  *    patterns, may be set for itemPtr.
  364.  *
  365.  *--------------------------------------------------------------
  366.  */
  367.  
  368. static int
  369. ConfigureLine(canvasPtr, itemPtr, argc, argv, flags)
  370.     Tk_Canvas *canvasPtr;    /* Canvas containing itemPtr. */
  371.     Tk_Item *itemPtr;        /* Line item to reconfigure. */
  372.     int argc;            /* Number of elements in argv.  */
  373.     char **argv;        /* Arguments describing things to configure. */
  374.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  375. {
  376.     register LineItem *linePtr = (LineItem *) itemPtr;
  377.     XGCValues gcValues;
  378.     GC newGC;
  379.     unsigned long mask;
  380.  
  381.     if (Tk_ConfigureWidget(canvasPtr->interp, canvasPtr->tkwin,
  382.         configSpecs, argc, argv, (char *) linePtr, flags) != TCL_OK) {
  383.     return TCL_ERROR;
  384.     }
  385.  
  386.     /*
  387.      * A few of the options require additional processing, such as
  388.      * graphics contexts.
  389.      */
  390.  
  391.     if (linePtr->fg == NULL) {
  392.     newGC = None;
  393.     } else {
  394.     gcValues.foreground = linePtr->fg->pixel;
  395.     gcValues.join_style = linePtr->joinStyle;
  396.     if (linePtr->width < 0) {
  397.         linePtr->width = 1;
  398.     }
  399.     gcValues.line_width = linePtr->width;
  400.     mask = GCForeground|GCJoinStyle|GCLineWidth;
  401.     if (linePtr->fillStipple != None) {
  402.         gcValues.stipple = linePtr->fillStipple;
  403.         gcValues.fill_style = FillStippled;
  404.         mask |= GCStipple|GCFillStyle;
  405.     }
  406.     if (linePtr->arrow == noneUid) {
  407.         gcValues.cap_style = linePtr->capStyle;
  408.         mask |= GCCapStyle;
  409.     }
  410.     newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues);
  411.     }
  412.     if (linePtr->gc != None) {
  413.     Tk_FreeGC(linePtr->gc);
  414.     }
  415.     linePtr->gc = newGC;
  416.  
  417.     /*
  418.      * Keep spline parameters within reasonable limits.
  419.      */
  420.  
  421.     if (linePtr->splineSteps < 1) {
  422.     linePtr->splineSteps = 1;
  423.     } else if (linePtr->splineSteps > 100) {
  424.     linePtr->splineSteps = 100;
  425.     }
  426.  
  427.     /*
  428.      * Setup arrowheads, if needed.  If arrowheads are turned off,
  429.      * restore the line's endpoints (they were shortened when the
  430.      * arrowheads were added).
  431.      */
  432.  
  433.     if ((linePtr->firstArrowPtr != NULL) && (linePtr->arrow != firstUid)
  434.         && (linePtr->arrow != bothUid)) {
  435.     linePtr->coordPtr[0] = linePtr->firstArrowPtr[0];
  436.     linePtr->coordPtr[1] = linePtr->firstArrowPtr[1];
  437.     ckfree((char *) linePtr->firstArrowPtr);
  438.     linePtr->firstArrowPtr = NULL;
  439.     }
  440.     if ((linePtr->lastArrowPtr != NULL) && (linePtr->arrow != lastUid)
  441.         && (linePtr->arrow != bothUid)) {
  442.     int index;
  443.  
  444.     index = 2*(linePtr->numPoints-1);
  445.     linePtr->coordPtr[index] = linePtr->lastArrowPtr[0];
  446.     linePtr->coordPtr[index+1] = linePtr->lastArrowPtr[1];
  447.     ckfree((char *) linePtr->lastArrowPtr);
  448.     linePtr->lastArrowPtr = NULL;
  449.     }
  450.     if (linePtr->arrow != noneUid) {
  451.     if ((linePtr->arrow != firstUid) && (linePtr->arrow != lastUid)
  452.         && (linePtr->arrow != bothUid)) {
  453.         Tcl_AppendResult(canvasPtr->interp, "bad arrow spec \"",
  454.             linePtr->arrow, "\": must be none, first, last, or both",
  455.             (char *) NULL);
  456.         linePtr->arrow = noneUid;
  457.         return TCL_ERROR;
  458.     }
  459.     ConfigureArrows(canvasPtr, linePtr);
  460.     }
  461.  
  462.     /*
  463.      * Recompute bounding box for line.
  464.      */
  465.  
  466.     ComputeLineBbox(canvasPtr, linePtr);
  467.  
  468.     return TCL_OK;
  469. }
  470.  
  471. /*
  472.  *--------------------------------------------------------------
  473.  *
  474.  * DeleteLine --
  475.  *
  476.  *    This procedure is called to clean up the data structure
  477.  *    associated with a line item.
  478.  *
  479.  * Results:
  480.  *    None.
  481.  *
  482.  * Side effects:
  483.  *    Resources associated with itemPtr are released.
  484.  *
  485.  *--------------------------------------------------------------
  486.  */
  487.  
  488. static void
  489. DeleteLine(itemPtr)
  490.     Tk_Item *itemPtr;            /* Item that is being deleted. */
  491. {
  492.     register LineItem *linePtr = (LineItem *) itemPtr;
  493.  
  494.     if (linePtr->coordPtr != NULL) {
  495.     ckfree((char *) linePtr->coordPtr);
  496.     }
  497.     if (linePtr->fg != NULL) {
  498.     Tk_FreeColor(linePtr->fg);
  499.     }
  500.     if (linePtr->fillStipple != None) {
  501.     Tk_FreeBitmap(linePtr->fillStipple);
  502.     }
  503.     if (linePtr->gc != None) {
  504.     Tk_FreeGC(linePtr->gc);
  505.     }
  506.     if (linePtr->firstArrowPtr != NULL) {
  507.     ckfree((char *) linePtr->firstArrowPtr);
  508.     }
  509.     if (linePtr->lastArrowPtr != NULL) {
  510.     ckfree((char *) linePtr->lastArrowPtr);
  511.     }
  512. }
  513.  
  514. /*
  515.  *--------------------------------------------------------------
  516.  *
  517.  * ComputeLineBbox --
  518.  *
  519.  *    This procedure is invoked to compute the bounding box of
  520.  *    all the pixels that may be drawn as part of a line.
  521.  *
  522.  * Results:
  523.  *    None.
  524.  *
  525.  * Side effects:
  526.  *    The fields x1, y1, x2, and y2 are updated in the header
  527.  *    for itemPtr.
  528.  *
  529.  *--------------------------------------------------------------
  530.  */
  531.  
  532. static void
  533. ComputeLineBbox(canvasPtr, linePtr)
  534.     register Tk_Canvas *canvasPtr;    /* Canvas that contains item. */
  535.     LineItem *linePtr;            /* Item whose bbos is to be
  536.                      * recomputed. */
  537. {
  538.     register double *coordPtr;
  539.     int i;
  540.  
  541.     coordPtr = linePtr->coordPtr;
  542.     linePtr->header.x1 = linePtr->header.x2 = *coordPtr;
  543.     linePtr->header.y1 = linePtr->header.y2 = coordPtr[1];
  544.  
  545.     /*
  546.      * Compute the bounding box of all the points in the line,
  547.      * then expand in all directions by the line's width to take
  548.      * care of butting or rounded corners and projecting or
  549.      * rounded caps.  This expansion is an overestimate (worst-case
  550.      * is square root of two over two) but it's simple.  Don't do
  551.      * anything special for curves.  This causes an additional
  552.      * overestimate in the bounding box, but is faster.
  553.      */
  554.  
  555.     for (i = 1, coordPtr = linePtr->coordPtr+2; i < linePtr->numPoints;
  556.         i++, coordPtr += 2) {
  557.     TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, coordPtr);
  558.     }
  559.     linePtr->header.x1 -= linePtr->width;
  560.     linePtr->header.x2 += linePtr->width;
  561.     linePtr->header.y1 -= linePtr->width;
  562.     linePtr->header.y2 += linePtr->width;
  563.  
  564.     /*
  565.      * For mitered lines, make a second pass through all the points.
  566.      * Compute the locations of the two miter vertex points and add
  567.      * those into the bounding box.
  568.      */
  569.  
  570.     if (linePtr->joinStyle == JoinMiter) {
  571.     for (i = linePtr->numPoints, coordPtr = linePtr->coordPtr; i >= 3;
  572.         i--, coordPtr += 2) {
  573.         double miter[4];
  574.         int j;
  575.     
  576.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  577.             (double) linePtr->width, miter, miter+2)) {
  578.         for (j = 0; j < 4; j += 2) {
  579.             TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, miter+j);
  580.         }
  581.         }
  582.     }
  583.     }
  584.  
  585.     /*
  586.      * Add in the sizes of arrowheads, if any.
  587.      */
  588.  
  589.     if (linePtr->arrow != noneUid) {
  590.     if (linePtr->arrow != lastUid) {
  591.         for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  592.             i++, coordPtr += 2) {
  593.         TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, coordPtr);
  594.         }
  595.     }
  596.     if (linePtr->arrow != firstUid) {
  597.         for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  598.             i++, coordPtr += 2) {
  599.         TkIncludePoint(canvasPtr, (Tk_Item *) linePtr, coordPtr);
  600.         }
  601.     }
  602.     }
  603.  
  604.     /*
  605.      * Add one more pixel of fudge factor just to be safe (e.g.
  606.      * X may round differently than we do).
  607.      */
  608.  
  609.     linePtr->header.x1 -= 1;
  610.     linePtr->header.x2 += 1;
  611.     linePtr->header.y1 -= 1;
  612.     linePtr->header.y2 += 1;
  613. }
  614.  
  615. /*
  616.  *--------------------------------------------------------------
  617.  *
  618.  * DisplayLine --
  619.  *
  620.  *    This procedure is invoked to draw a line item in a given
  621.  *    drawable.
  622.  *
  623.  * Results:
  624.  *    None.
  625.  *
  626.  * Side effects:
  627.  *    ItemPtr is drawn in drawable using the transformation
  628.  *    information in canvasPtr.
  629.  *
  630.  *--------------------------------------------------------------
  631.  */
  632.  
  633. static void
  634. DisplayLine(canvasPtr, itemPtr, drawable)
  635.     register Tk_Canvas *canvasPtr;    /* Canvas that contains item. */
  636.     Tk_Item *itemPtr;            /* Item to be displayed. */
  637.     Drawable drawable;            /* Pixmap or window in which to draw
  638.                      * item. */
  639. {
  640.     register LineItem *linePtr = (LineItem *) itemPtr;
  641.     XPoint staticPoints[MAX_STATIC_POINTS];
  642.     XPoint *pointPtr;
  643.     register XPoint *pPtr;
  644.     register double *coordPtr;
  645.     int i, numPoints;
  646.  
  647.     if (linePtr->gc == None) {
  648.     return;
  649.     }
  650.  
  651.     /*
  652.      * Build up an array of points in screen coordinates.  Use a
  653.      * static array unless the line has an enormous number of points;
  654.      * in this case, dynamically allocate an array.  For smoothed lines,
  655.      * generate the curve points on each redisplay.
  656.      */
  657.  
  658.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  659.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  660.     } else {
  661.     numPoints = linePtr->numPoints;
  662.     }
  663.  
  664.     if (numPoints <= MAX_STATIC_POINTS) {
  665.     pointPtr = staticPoints;
  666.     } else {
  667.     pointPtr = (XPoint *) ckalloc((unsigned) (numPoints * sizeof(XPoint)));
  668.     }
  669.  
  670.     if (linePtr->smooth) {
  671.     numPoints = TkMakeBezierCurve(canvasPtr, linePtr->coordPtr,
  672.         linePtr->numPoints, linePtr->splineSteps, pointPtr,
  673.         (double *) NULL);
  674.     } else {
  675.     for (i = 0, coordPtr = linePtr->coordPtr, pPtr = pointPtr;
  676.         i < linePtr->numPoints;  i += 1, coordPtr += 2, pPtr++) {
  677.         pPtr->x = SCREEN_X(canvasPtr, *coordPtr);
  678.         pPtr->y = SCREEN_Y(canvasPtr, coordPtr[1]);
  679.     }
  680.     }
  681.  
  682.     /*
  683.      * Display line, the free up line storage if it was dynamically
  684.      * allocated.
  685.      */
  686.  
  687.     XDrawLines(Tk_Display(canvasPtr->tkwin), drawable, linePtr->gc,
  688.         pointPtr, numPoints, CoordModeOrigin);
  689.     if (pointPtr != staticPoints) {
  690.     ckfree((char *) pointPtr);
  691.     }
  692.  
  693.     /*
  694.      * Display arrowheads, if they are wanted.
  695.      */
  696.  
  697.     if (linePtr->arrow != noneUid) {
  698.     if (linePtr->arrow != lastUid) {
  699.         TkFillPolygon(canvasPtr, linePtr->firstArrowPtr, PTS_IN_ARROW,
  700.             drawable, linePtr->gc);
  701.     }
  702.     if (linePtr->arrow != firstUid) {
  703.         TkFillPolygon(canvasPtr, linePtr->lastArrowPtr, PTS_IN_ARROW,
  704.             drawable, linePtr->gc);
  705.     }
  706.     }
  707. }
  708.  
  709. /*
  710.  *--------------------------------------------------------------
  711.  *
  712.  * LineToPoint --
  713.  *
  714.  *    Computes the distance from a given point to a given
  715.  *    line, in canvas units.
  716.  *
  717.  * Results:
  718.  *    The return value is 0 if the point whose x and y coordinates
  719.  *    are pointPtr[0] and pointPtr[1] is inside the line.  If the
  720.  *    point isn't inside the line then the return value is the
  721.  *    distance from the point to the line.
  722.  *
  723.  * Side effects:
  724.  *    None.
  725.  *
  726.  *--------------------------------------------------------------
  727.  */
  728.  
  729.     /* ARGSUSED */
  730. static double
  731. LineToPoint(canvasPtr, itemPtr, pointPtr)
  732.     Tk_Canvas *canvasPtr;    /* Canvas containing item. */
  733.     Tk_Item *itemPtr;        /* Item to check against point. */
  734.     double *pointPtr;        /* Pointer to x and y coordinates. */
  735. {
  736.     register LineItem *linePtr = (LineItem *) itemPtr;
  737.     register double *coordPtr, *linePoints;
  738.     double staticSpace[2*MAX_STATIC_POINTS];
  739.     double poly[10];
  740.     double bestDist, dist;
  741.     int numPoints, count;
  742.     int changedMiterToBevel;    /* Non-zero means that a mitered corner
  743.                  * had to be treated as beveled after all
  744.                  * because the angle was < 11 degrees. */
  745.  
  746.     bestDist = 1.0e40;
  747.  
  748.     /*
  749.      * Handle smoothed lines by generating an expanded set of points
  750.      * against which to do the check.
  751.      */
  752.  
  753.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  754.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  755.     if (numPoints <= MAX_STATIC_POINTS) {
  756.         linePoints = staticSpace;
  757.     } else {
  758.         linePoints = (double *) ckalloc((unsigned)
  759.             (2*numPoints*sizeof(double)));
  760.     }
  761.     numPoints = TkMakeBezierCurve(canvasPtr, linePtr->coordPtr,
  762.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  763.         linePoints);
  764.     } else {
  765.     numPoints = linePtr->numPoints;
  766.     linePoints = linePtr->coordPtr;
  767.     }
  768.  
  769.     /*
  770.      * The overall idea is to iterate through all of the edges of
  771.      * the line, computing a polygon for each edge and testing the
  772.      * point against that polygon.  In addition, there are additional
  773.      * tests to deal with rounded joints and caps.
  774.      */
  775.  
  776.     changedMiterToBevel = 0;
  777.     for (count = numPoints, coordPtr = linePoints; count >= 2;
  778.         count--, coordPtr += 2) {
  779.  
  780.     /*
  781.      * If rounding is done around the first point then compute
  782.      * the distance between the point and the point.
  783.      */
  784.  
  785.     if (((linePtr->capStyle == CapRound) && (count == numPoints))
  786.         || ((linePtr->joinStyle == JoinRound)
  787.             && (count != numPoints))) {
  788.         dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  789.             - linePtr->width/2.0;
  790.         if (dist <= 0.0) {
  791.         bestDist = 0.0;
  792.         goto done;
  793.         } else if (dist < bestDist) {
  794.         bestDist = dist;
  795.         }
  796.     }
  797.  
  798.     /*
  799.      * Compute the polygonal shape corresponding to this edge,
  800.      * consisting of two points for the first point of the edge
  801.      * and two points for the last point of the edge.
  802.      */
  803.  
  804.     if (count == numPoints) {
  805.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width,
  806.             linePtr->capStyle == CapProjecting, poly, poly+2);
  807.     } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
  808.         poly[0] = poly[6];
  809.         poly[1] = poly[7];
  810.         poly[2] = poly[4];
  811.         poly[3] = poly[5];
  812.     } else {
  813.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0,
  814.             poly, poly+2);
  815.  
  816.         /*
  817.          * If this line uses beveled joints, then check the distance
  818.          * to a polygon comprising the last two points of the previous
  819.          * polygon and the first two from this polygon;  this checks
  820.          * the wedges that fill the mitered joint.
  821.          */
  822.  
  823.         if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
  824.         poly[8] = poly[0];
  825.         poly[9] = poly[1];
  826.         dist = TkPolygonToPoint(poly, 5, pointPtr);
  827.         if (dist <= 0.0) {
  828.             bestDist = 0.0;
  829.             goto done;
  830.         } else if (dist < bestDist) {
  831.             bestDist = dist;
  832.         }
  833.         changedMiterToBevel = 0;
  834.         }
  835.     }
  836.     if (count == 2) {
  837.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  838.             linePtr->capStyle == CapProjecting, poly+4, poly+6);
  839.     } else if (linePtr->joinStyle == JoinMiter) {
  840.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  841.             (double) linePtr->width, poly+4, poly+6) == 0) {
  842.         changedMiterToBevel = 1;
  843.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  844.             0, poly+4, poly+6);
  845.         }
  846.     } else {
  847.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0,
  848.             poly+4, poly+6);
  849.     }
  850.     poly[8] = poly[0];
  851.     poly[9] = poly[1];
  852.     dist = TkPolygonToPoint(poly, 5, pointPtr);
  853.     if (dist <= 0.0) {
  854.         bestDist = 0.0;
  855.         goto done;
  856.     } else if (dist < bestDist) {
  857.         bestDist = dist;
  858.     }
  859.     }
  860.  
  861.     /*
  862.      * If caps are rounded, check the distance to the cap around the
  863.      * final end point of the line.
  864.      */
  865.  
  866.     if (linePtr->capStyle == CapRound) {
  867.     dist = hypot(coordPtr[0] - pointPtr[0], coordPtr[1] - pointPtr[1])
  868.         - linePtr->width/2.0;
  869.     if (dist <= 0.0) {
  870.         bestDist = 0.0;
  871.         goto done;
  872.     } else if (dist < bestDist) {
  873.         bestDist = dist;
  874.     }
  875.     }
  876.  
  877.     /*
  878.      * If there are arrowheads, check the distance to the arrowheads.
  879.      */
  880.  
  881.     if (linePtr->arrow != noneUid) {
  882.     if (linePtr->arrow != lastUid) {
  883.         dist = TkPolygonToPoint(linePtr->firstArrowPtr, PTS_IN_ARROW,
  884.             pointPtr);
  885.         if (dist <= 0.0) {
  886.         bestDist = 0.0;
  887.         goto done;
  888.         } else if (dist < bestDist) {
  889.         bestDist = dist;
  890.         }
  891.     }
  892.     if (linePtr->arrow != firstUid) {
  893.         dist = TkPolygonToPoint(linePtr->lastArrowPtr, PTS_IN_ARROW,
  894.             pointPtr);
  895.         if (dist <= 0.0) {
  896.         bestDist = 0.0;
  897.         goto done;
  898.         } else if (dist < bestDist) {
  899.         bestDist = dist;
  900.         }
  901.     }
  902.     }
  903.  
  904.     done:
  905.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  906.     ckfree((char *) linePoints);
  907.     }
  908.     return bestDist;
  909. }
  910.  
  911. /*
  912.  *--------------------------------------------------------------
  913.  *
  914.  * LineToArea --
  915.  *
  916.  *    This procedure is called to determine whether an item
  917.  *    lies entirely inside, entirely outside, or overlapping
  918.  *    a given rectangular area.
  919.  *
  920.  * Results:
  921.  *    -1 is returned if the item is entirely outside the
  922.  *    area, 0 if it overlaps, and 1 if it is entirely
  923.  *    inside the given area.
  924.  *
  925.  * Side effects:
  926.  *    None.
  927.  *
  928.  *--------------------------------------------------------------
  929.  */
  930.  
  931.     /* ARGSUSED */
  932. static int
  933. LineToArea(canvasPtr, itemPtr, rectPtr)
  934.     Tk_Canvas *canvasPtr;    /* Canvas containing item. */
  935.     Tk_Item *itemPtr;        /* Item to check against line. */
  936.     double *rectPtr;
  937. {
  938.     register LineItem *linePtr = (LineItem *) itemPtr;
  939.     register double *coordPtr;
  940.     double staticSpace[2*MAX_STATIC_POINTS];
  941.     double *linePoints, poly[10];
  942.     double radius;
  943.     int numPoints, count;
  944.     int changedMiterToBevel;    /* Non-zero means that a mitered corner
  945.                  * had to be treated as beveled after all
  946.                  * because the angle was < 11 degrees. */
  947.     int inside;            /* Tentative guess about what to return,
  948.                  * based on all points seen so far:  one
  949.                  * means everything seen so far was
  950.                  * inside the area;  -1 means everything
  951.                  * was outside the area.  0 means overlap
  952.                  * has been found. */ 
  953.  
  954.     radius = linePtr->width/2.0;
  955.     inside = -1;
  956.  
  957.     /*
  958.      * Handle smoothed lines by generating an expanded set of points
  959.      * against which to do the check.
  960.      */
  961.  
  962.     if ((linePtr->smooth) && (linePtr->numPoints > 2)) {
  963.     numPoints = 1 + linePtr->numPoints*linePtr->splineSteps;
  964.     if (numPoints <= MAX_STATIC_POINTS) {
  965.         linePoints = staticSpace;
  966.     } else {
  967.         linePoints = (double *) ckalloc((unsigned)
  968.             (2*numPoints*sizeof(double)));
  969.     }
  970.     numPoints = TkMakeBezierCurve(canvasPtr, linePtr->coordPtr,
  971.         linePtr->numPoints, linePtr->splineSteps, (XPoint *) NULL,
  972.         linePoints);
  973.     } else {
  974.     numPoints = linePtr->numPoints;
  975.     linePoints = linePtr->coordPtr;
  976.     }
  977.  
  978.     coordPtr = linePoints;
  979.     if ((coordPtr[0] >= rectPtr[0]) && (coordPtr[0] <= rectPtr[2])
  980.         && (coordPtr[1] >= rectPtr[1]) && (coordPtr[1] <= rectPtr[3])) {
  981.     inside = 1;
  982.     }
  983.  
  984.     /*
  985.      * Iterate through all of the edges of the line, computing a polygon
  986.      * for each edge and testing the area against that polygon.  In
  987.      * addition, there are additional tests to deal with rounded joints
  988.      * and caps.
  989.      */
  990.  
  991.     changedMiterToBevel = 0;
  992.     for (count = numPoints; count >= 2; count--, coordPtr += 2) {
  993.  
  994.     /*
  995.      * If rounding is done around the first point of the edge
  996.      * then test a circular region around the point with the
  997.      * area.
  998.      */
  999.  
  1000.     if (((linePtr->capStyle == CapRound) && (count == numPoints))
  1001.         || ((linePtr->joinStyle == JoinRound)
  1002.         && (count != numPoints))) {
  1003.         poly[0] = coordPtr[0] - radius;
  1004.         poly[1] = coordPtr[1] - radius;
  1005.         poly[2] = coordPtr[0] + radius;
  1006.         poly[3] = coordPtr[1] + radius;
  1007.         if (TkOvalToArea(poly, rectPtr) != inside) {
  1008.         inside = 0;
  1009.         goto done;
  1010.         }
  1011.     }
  1012.  
  1013.     /*
  1014.      * Compute the polygonal shape corresponding to this edge,
  1015.      * consisting of two points for the first point of the edge
  1016.      * and two points for the last point of the edge.
  1017.      */
  1018.  
  1019.     if (count == numPoints) {
  1020.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width,
  1021.             linePtr->capStyle == CapProjecting, poly, poly+2);
  1022.     } else if ((linePtr->joinStyle == JoinMiter) && !changedMiterToBevel) {
  1023.         poly[0] = poly[6];
  1024.         poly[1] = poly[7];
  1025.         poly[2] = poly[4];
  1026.         poly[3] = poly[5];
  1027.     } else {
  1028.         TkGetButtPoints(coordPtr+2, coordPtr, (double) linePtr->width, 0,
  1029.             poly, poly+2);
  1030.  
  1031.         /*
  1032.          * If the last joint was beveled, then also check a
  1033.          * polygon comprising the last two points of the previous
  1034.          * polygon and the first two from this polygon;  this checks
  1035.          * the wedges that fill the beveled joint.
  1036.          */
  1037.  
  1038.         if ((linePtr->joinStyle == JoinBevel) || changedMiterToBevel) {
  1039.         poly[8] = poly[0];
  1040.         poly[9] = poly[1];
  1041.         if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
  1042.             inside = 0;
  1043.             goto done;
  1044.         }
  1045.         changedMiterToBevel = 0;
  1046.         }
  1047.     }
  1048.     if (count == 2) {
  1049.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  1050.             linePtr->capStyle == CapProjecting, poly+4, poly+6);
  1051.     } else if (linePtr->joinStyle == JoinMiter) {
  1052.         if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  1053.             (double) linePtr->width, poly+4, poly+6) == 0) {
  1054.         changedMiterToBevel = 1;
  1055.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width,
  1056.             0, poly+4, poly+6);
  1057.         }
  1058.     } else {
  1059.         TkGetButtPoints(coordPtr, coordPtr+2, (double) linePtr->width, 0,
  1060.             poly+4, poly+6);
  1061.     }
  1062.     poly[8] = poly[0];
  1063.     poly[9] = poly[1];
  1064.     if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
  1065.         inside = 0;
  1066.         goto done;
  1067.     }
  1068.     }
  1069.  
  1070.     /*
  1071.      * If caps are rounded, check the cap around the final point
  1072.      * of the line.
  1073.      */
  1074.  
  1075.     if (linePtr->capStyle == CapRound) {
  1076.     poly[0] = coordPtr[0] - radius;
  1077.     poly[1] = coordPtr[1] - radius;
  1078.     poly[2] = coordPtr[0] + radius;
  1079.     poly[3] = coordPtr[1] + radius;
  1080.     if (TkOvalToArea(poly, rectPtr) != inside) {
  1081.         inside = 0;
  1082.         goto done;
  1083.     }
  1084.     }
  1085.  
  1086.     /*
  1087.      * Check arrowheads, if any.
  1088.      */
  1089.  
  1090.     if (linePtr->arrow != noneUid) {
  1091.     if (linePtr->arrow != lastUid) {
  1092.         if (TkPolygonToArea(linePtr->firstArrowPtr, PTS_IN_ARROW,
  1093.             rectPtr) != inside) {
  1094.         inside = 0;
  1095.         goto done;
  1096.         }
  1097.     }
  1098.     if (linePtr->arrow != firstUid) {
  1099.         if (TkPolygonToArea(linePtr->lastArrowPtr, PTS_IN_ARROW,
  1100.             rectPtr) != inside) {
  1101.         inside = 0;
  1102.         goto done;
  1103.         }
  1104.     }
  1105.     }
  1106.  
  1107.     done:
  1108.     if ((linePoints != staticSpace) && (linePoints != linePtr->coordPtr)) {
  1109.     ckfree((char *) linePoints);
  1110.     }
  1111.     return inside;
  1112. }
  1113.  
  1114. /*
  1115.  *--------------------------------------------------------------
  1116.  *
  1117.  * ScaleLine --
  1118.  *
  1119.  *    This procedure is invoked to rescale a line item.
  1120.  *
  1121.  * Results:
  1122.  *    None.
  1123.  *
  1124.  * Side effects:
  1125.  *    The line referred to by itemPtr is rescaled so that the
  1126.  *    following transformation is applied to all point
  1127.  *    coordinates:
  1128.  *        x' = originX + scaleX*(x-originX)
  1129.  *        y' = originY + scaleY*(y-originY)
  1130.  *
  1131.  *--------------------------------------------------------------
  1132.  */
  1133.  
  1134. static void
  1135. ScaleLine(canvasPtr, itemPtr, originX, originY, scaleX, scaleY)
  1136.     Tk_Canvas *canvasPtr;        /* Canvas containing line. */
  1137.     Tk_Item *itemPtr;            /* Line to be scaled. */
  1138.     double originX, originY;        /* Origin about which to scale rect. */
  1139.     double scaleX;            /* Amount to scale in X direction. */
  1140.     double scaleY;            /* Amount to scale in Y direction. */
  1141. {
  1142.     LineItem *linePtr = (LineItem *) itemPtr;
  1143.     register double *coordPtr;
  1144.     int i;
  1145.  
  1146.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1147.         i++, coordPtr += 2) {
  1148.     coordPtr[0] = originX + scaleX*(*coordPtr - originX);
  1149.     coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
  1150.     }
  1151.     if (linePtr->firstArrowPtr != NULL) {
  1152.     for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  1153.         i++, coordPtr += 2) {
  1154.         coordPtr[0] = originX + scaleX*(coordPtr[0] - originX);
  1155.         coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
  1156.     }
  1157.     }
  1158.     if (linePtr->lastArrowPtr != NULL) {
  1159.     for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  1160.         i++, coordPtr += 2) {
  1161.         coordPtr[0] = originX + scaleX*(coordPtr[0] - originX);
  1162.         coordPtr[1] = originY + scaleY*(coordPtr[1] - originY);
  1163.     }
  1164.     }
  1165.     ComputeLineBbox(canvasPtr, linePtr);
  1166. }
  1167.  
  1168. /*
  1169.  *--------------------------------------------------------------
  1170.  *
  1171.  * TranslateLine --
  1172.  *
  1173.  *    This procedure is called to move a line by a given amount.
  1174.  *
  1175.  * Results:
  1176.  *    None.
  1177.  *
  1178.  * Side effects:
  1179.  *    The position of the line is offset by (xDelta, yDelta), and
  1180.  *    the bounding box is updated in the generic part of the item
  1181.  *    structure.
  1182.  *
  1183.  *--------------------------------------------------------------
  1184.  */
  1185.  
  1186. static void
  1187. TranslateLine(canvasPtr, itemPtr, deltaX, deltaY)
  1188.     Tk_Canvas *canvasPtr;        /* Canvas containing item. */
  1189.     Tk_Item *itemPtr;            /* Item that is being moved. */
  1190.     double deltaX, deltaY;        /* Amount by which item is to be
  1191.                      * moved. */
  1192. {
  1193.     LineItem *linePtr = (LineItem *) itemPtr;
  1194.     register double *coordPtr;
  1195.     int i;
  1196.  
  1197.     for (i = 0, coordPtr = linePtr->coordPtr; i < linePtr->numPoints;
  1198.         i++, coordPtr += 2) {
  1199.     coordPtr[0] += deltaX;
  1200.     coordPtr[1] += deltaY;
  1201.     }
  1202.     if (linePtr->firstArrowPtr != NULL) {
  1203.     for (i = 0, coordPtr = linePtr->firstArrowPtr; i < PTS_IN_ARROW;
  1204.         i++, coordPtr += 2) {
  1205.         coordPtr[0] += deltaX;
  1206.         coordPtr[1] += deltaY;
  1207.     }
  1208.     }
  1209.     if (linePtr->lastArrowPtr != NULL) {
  1210.     for (i = 0, coordPtr = linePtr->lastArrowPtr; i < PTS_IN_ARROW;
  1211.         i++, coordPtr += 2) {
  1212.         coordPtr[0] += deltaX;
  1213.         coordPtr[1] += deltaY;
  1214.     }
  1215.     }
  1216.     ComputeLineBbox(canvasPtr, linePtr);
  1217. }
  1218.  
  1219. /*
  1220.  *--------------------------------------------------------------
  1221.  *
  1222.  * ParseArrowShape --
  1223.  *
  1224.  *    This procedure is called back during option parsing to
  1225.  *    parse arrow shape information.
  1226.  *
  1227.  * Results:
  1228.  *    The return value is a standard Tcl result:  TCL_OK means
  1229.  *    that the arrow shape information was parsed ok, and
  1230.  *    TCL_ERROR means it couldn't be parsed.
  1231.  *
  1232.  * Side effects:
  1233.  *    Arrow information in recordPtr is updated.
  1234.  *
  1235.  *--------------------------------------------------------------
  1236.  */
  1237.  
  1238.     /* ARGSUSED */
  1239. static int
  1240. ParseArrowShape(clientData, interp, tkwin, value, recordPtr, offset)
  1241.     ClientData clientData;    /* Not used. */
  1242.     Tcl_Interp *interp;        /* Used for error reporting. */
  1243.     Tk_Window tkwin;        /* Not used. */
  1244.     char *value;        /* Textual specification of arrow shape. */
  1245.     char *recordPtr;        /* Pointer to item record in which to
  1246.                  * store arrow information. */
  1247.     int offset;            /* Offset of shape information in widget
  1248.                  * record. */
  1249. {
  1250.     LineItem *linePtr = (LineItem *) recordPtr;
  1251.     double a, b, c;
  1252.     int argc;
  1253.     char **argv = NULL;
  1254.  
  1255.     if (offset != Tk_Offset(LineItem, arrowShapeA)) {
  1256.     panic("ParseArrowShape received bogus offset");
  1257.     }
  1258.  
  1259.     if (Tcl_SplitList(interp, value, &argc, &argv) != TCL_OK) {
  1260.     syntaxError:
  1261.     Tcl_ResetResult(interp);
  1262.     Tcl_AppendResult(interp, "bad arrow shape \"", value,
  1263.         "\": must be list with three numbers", (char *) NULL);
  1264.     if (argv != NULL) {
  1265.         ckfree((char *) argv);
  1266.     }
  1267.     return TCL_ERROR;
  1268.     }
  1269.     if (argc != 3) {
  1270.     goto syntaxError;
  1271.     }
  1272.     if ((TkGetCanvasCoord(linePtr->canvasPtr, argv[0], &a) != TCL_OK)
  1273.         || (TkGetCanvasCoord(linePtr->canvasPtr, argv[1], &b) != TCL_OK)
  1274.         || (TkGetCanvasCoord(linePtr->canvasPtr, argv[2], &c) != TCL_OK)) {
  1275.     goto syntaxError;
  1276.     }
  1277.     linePtr->arrowShapeA = a;
  1278.     linePtr->arrowShapeB = b;
  1279.     linePtr->arrowShapeC = c;
  1280.     ckfree((char *) argv);
  1281.     return TCL_OK;
  1282. }
  1283.  
  1284. /*
  1285.  *--------------------------------------------------------------
  1286.  *
  1287.  * PrintArrowShape --
  1288.  *
  1289.  *    This procedure is a callback invoked by the configuration
  1290.  *    code to return a printable value describing an arrow shape.
  1291.  *
  1292.  * Results:
  1293.  *    None.
  1294.  *
  1295.  * Side effects:
  1296.  *    None.
  1297.  *
  1298.  *--------------------------------------------------------------
  1299.  */
  1300.  
  1301.     /* ARGSUSED */
  1302. static char *
  1303. PrintArrowShape(clientData, tkwin, recordPtr, offset, freeProcPtr)
  1304.     ClientData clientData;    /* Not used. */
  1305.     Tk_Window tkwin;        /* Window associated with linePtr's widget. */
  1306.     char *recordPtr;        /* Pointer to item record containing current
  1307.                  * shape information. */
  1308.     int offset;            /* Offset of arrow information in record. */
  1309.     Tcl_FreeProc **freeProcPtr;    /* Store address of procedure to call to
  1310.                  * free string here. */
  1311. {
  1312.     LineItem *linePtr = (LineItem *) recordPtr;
  1313.     char *buffer;
  1314.  
  1315.     buffer = ckalloc(120);
  1316.     sprintf(buffer, "%.5g %.5g %.5g", linePtr->arrowShapeA,
  1317.         linePtr->arrowShapeB, linePtr->arrowShapeC);
  1318.     *freeProcPtr = (Tcl_FreeProc *) free;
  1319.     return buffer;
  1320. }
  1321.  
  1322. /*
  1323.  *--------------------------------------------------------------
  1324.  *
  1325.  * ConfigureArrows --
  1326.  *
  1327.  *    If arrowheads have been requested for a line, this
  1328.  *    procedure makes arrangements for the arrowheads.
  1329.  *
  1330.  * Results:
  1331.  *    A standard Tcl return value.  If an error occurs, then
  1332.  *    an error message is left in canvasPtr->interp->result.
  1333.  *
  1334.  * Side effects:
  1335.  *    Information in linePtr is set up for one or two arrowheads.
  1336.  *    the firstArrowPtr and lastArrowPtr polygons are allocated
  1337.  *    and initialized, if need be, and the end points of the line
  1338.  *    are adjusted so that a thick line doesn't stick out past
  1339.  *    the arrowheads.
  1340.  *
  1341.  *--------------------------------------------------------------
  1342.  */
  1343.  
  1344.     /* ARGSUSED */
  1345. static int
  1346. ConfigureArrows(canvasPtr, linePtr)
  1347.     Tk_Canvas *canvasPtr;        /* Canvas in which arrows will be
  1348.                      * displayed (interp and tkwin
  1349.                      * fields are needed). */
  1350.     register LineItem *linePtr;        /* Item to configure for arrows. */
  1351. {
  1352.     double *poly, *coordPtr;
  1353.     double dx, dy, length, sinTheta, cosTheta, temp, shapeC;
  1354.     double fracHeight;            /* Line width as fraction of
  1355.                      * arrowhead width. */
  1356.     double backup;            /* Distance to backup end points
  1357.                      * so the line ends in the middle
  1358.                      * of the arrowhead. */
  1359.     double vertX, vertY;        /* Position of arrowhead vertex. */
  1360.  
  1361.     /*
  1362.      * If there's an arrowhead on the first point of the line, compute
  1363.      * its polygon and adjust the first point of the line so that the
  1364.      * line doesn't stick out past the leading edge of the arrowhead.
  1365.      */
  1366.  
  1367.     shapeC = linePtr->arrowShapeC + linePtr->width/2.0;
  1368.     fracHeight = (linePtr->width/2.0)/shapeC;
  1369.     backup = fracHeight*linePtr->arrowShapeB
  1370.         + linePtr->arrowShapeA*(1.0 - fracHeight)/2.0;
  1371.     if (linePtr->arrow != lastUid) {
  1372.     poly = linePtr->firstArrowPtr;
  1373.     if (poly == NULL) {
  1374.         poly = (double *) ckalloc((unsigned)
  1375.             (2*PTS_IN_ARROW*sizeof(double)));
  1376.         poly[0] = poly[10] = linePtr->coordPtr[0];
  1377.         poly[1] = poly[11] = linePtr->coordPtr[1];
  1378.         linePtr->firstArrowPtr = poly;
  1379.     }
  1380.     dx = poly[0] - linePtr->coordPtr[2];
  1381.     dy = poly[1] - linePtr->coordPtr[3];
  1382.     length = hypot(dx, dy);
  1383.     if (length == 0) {
  1384.         sinTheta = cosTheta = 0.0;
  1385.     } else {
  1386.         sinTheta = dy/length;
  1387.         cosTheta = dx/length;
  1388.     }
  1389.     vertX = poly[0] - linePtr->arrowShapeA*cosTheta;
  1390.     vertY = poly[1] - linePtr->arrowShapeA*sinTheta;
  1391.     temp = shapeC*sinTheta;
  1392.     poly[2] = poly[0] - linePtr->arrowShapeB*cosTheta + temp;
  1393.     poly[8] = poly[2] - 2*temp;
  1394.     temp = shapeC*cosTheta;
  1395.     poly[3] = poly[1] - linePtr->arrowShapeB*sinTheta - temp;
  1396.     poly[9] = poly[3] + 2*temp;
  1397.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1398.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1399.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1400.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1401.  
  1402.     /*
  1403.      * Polygon done.  Now move the first point towards the second so
  1404.      * that the corners at the end of the line are inside the
  1405.      * arrowhead.
  1406.      */
  1407.  
  1408.     linePtr->coordPtr[0] = poly[0] - backup*cosTheta;
  1409.     linePtr->coordPtr[1] = poly[1] - backup*sinTheta;
  1410.     }
  1411.  
  1412.     /*
  1413.      * Similar arrowhead calculation for the last point of the line.
  1414.      */
  1415.  
  1416.     if (linePtr->arrow != firstUid) {
  1417.     coordPtr = linePtr->coordPtr + 2*(linePtr->numPoints-2);
  1418.     poly = linePtr->lastArrowPtr;
  1419.     if (poly == NULL) {
  1420.         poly = (double *) ckalloc((unsigned)
  1421.             (2*PTS_IN_ARROW*sizeof(double)));
  1422.         poly[0] = poly[10] = coordPtr[2];
  1423.         poly[1] = poly[11] = coordPtr[3];
  1424.         linePtr->lastArrowPtr = poly;
  1425.     }
  1426.     dx = poly[0] - coordPtr[0];
  1427.     dy = poly[1] - coordPtr[1];
  1428.     length = hypot(dx, dy);
  1429.     if (length == 0) {
  1430.         sinTheta = cosTheta = 0.0;
  1431.     } else {
  1432.         sinTheta = dy/length;
  1433.         cosTheta = dx/length;
  1434.     }
  1435.     vertX = poly[0] - linePtr->arrowShapeA*cosTheta;
  1436.     vertY = poly[1] - linePtr->arrowShapeA*sinTheta;
  1437.     temp = shapeC*sinTheta;
  1438.     poly[2] = poly[0] - linePtr->arrowShapeB*cosTheta + temp;
  1439.     poly[8] = poly[2] - 2*temp;
  1440.     temp = shapeC*cosTheta;
  1441.     poly[3] = poly[1] - linePtr->arrowShapeB*sinTheta - temp;
  1442.     poly[9] = poly[3] + 2*temp;
  1443.     poly[4] = poly[2]*fracHeight + vertX*(1.0-fracHeight);
  1444.     poly[5] = poly[3]*fracHeight + vertY*(1.0-fracHeight);
  1445.     poly[6] = poly[8]*fracHeight + vertX*(1.0-fracHeight);
  1446.     poly[7] = poly[9]*fracHeight + vertY*(1.0-fracHeight);
  1447.     coordPtr[2] = poly[0] - backup*cosTheta;
  1448.     coordPtr[3] = poly[1] - backup*sinTheta;
  1449.     }
  1450.  
  1451.     return TCL_OK;
  1452. }
  1453.